/*
 * Backup Tool
 *
 * Encrypted backup using H2 database's AES encryption.
 *
 * Backup would be save in sql file format. Backup files would be stored in 'backup' folder.
 *
 * File format of backup encrypted SQL
 *
 * > First line is comment on the date and time of creation of backup file
 * > Second line is an SQL command to clean delete any remaining data in database before restoration of backup.
 * > Third line and on wards are AES encrypted hexadecimal string of insert statements containing data.
 *
 * License: LGPL v3
 */
package org.passwordstore.util;

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.Writer;
import java.util.ArrayList;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.passwordstore.Resource;

/**
 *
 * @author thotheolh
 */
public class Backup {

    private Resource res;
    SQLManager sql = null;
    private String bkpath = "backup";
    private String backupName = "backup";
    private int currIncrementNum, biggestIncNum, smallestIncNum = 0;

    public Backup(Resource res) {
        this.sql = res.getSql();
        this.res = res;
        //TODO: Encrypt backup content before backing to file...
    }

    /**
     * Main backup method to be called to execute backup.
     * @return String of backup filepath
     */
    public String doBackup() {
        ArrayList list = sql.viewAllPasswordsWPass();
        if (!list.isEmpty()) {
            return createBackupScript(list,null);
        } else {
            return "empty";
        }
    }

    /**
     * Method for creating the backup SQL script.
     * @param list, An arraylist of data results from database for backup
     * @return String of backup filepath
     */
    public String createBackupScript(ArrayList list, String currentDate) {
        try {
            checkDir(); //Check if proper directory is available

            this.getLatestBackupNum(); //Find the latest backup num

            StringBuffer buff = new StringBuffer(); //entire stringbuffer
            StringBuffer data = new StringBuffer(); //only for data

            Password pass; //Password helper class

            String currentDT;
            if (currentDate == null) {
                java.util.Date today = new java.util.Date();  //Getting current Data/Time
                currentDT = today.toString();
            } else {
                currentDT = currentDate;
            }

            buff.append("-- " + currentDT + "\n"); //Comment on date/time so user can differentiate the backup files themselves

            buff.append("DELETE FROM PASSWORDSTORE;\n"); // Before restoring, need to empty database first

            for (int i = 0; i < list.size(); i++) { //Populating data StringBuffer
                pass = (Password) list.get(i);
                data.append("INSERT INTO PASSWORDSTORE VALUES ('" + pass.getUname() + "','" + pass.getUpass() + "','" + pass.getRplace() + "');");
            }
            //System.out.println(buff.toString()); //Diagnostics

            //Encrypt data buffer and merging data to buff
            byte[] cipherBytes = null;
            byte[] passBytes = null;
            try {
                String p = String.valueOf(getRes().getPassword());
                passBytes = getRes().getAescrypt().hashpwd(p);
                cipherBytes = getRes().getAescrypt().encryptFile(data.toString(), passBytes);
            } catch (NullPointerException ex) {
                ex.printStackTrace();
            }
            buff.append(getRes().getAescrypt().ByteToHex(cipherBytes));

            //Writing to file
            Writer output = null;
            this.setBiggestIncNum(biggestIncNum + 1);
            String backup = this.getBkpath() + "/backup-" + this.getBiggestIncNum() + ".sql";
            File file = new File(backup);
            output = new BufferedWriter(new FileWriter(file));

            //Write output
            output.write(buff.toString());
            output.close();

            //System.out.println("Backup to: " + file.getAbsolutePath()); //Diagnostics

            return file.getAbsolutePath();
        } catch (IOException ex) {
            Logger.getLogger(Backup.class.getName()).log(Level.SEVERE, null, ex);
            return null;
        }
    }

    /**
     * Automatically probe for appropriate backup version number.
     * e.g. 'backup-1.sql' means that it's the first sql backup and the number would increment.
     * Use to prevent filename collisions.
     */
    public void getLatestBackupNum() {
        File file = new File(this.getBkpath());
        String[] filelist = file.list();
        if (filelist.length > 0) {
            for (int i = 0; i < filelist.length; i++) {
                String currFileName = filelist[i].toString();
                String[] filename = currFileName.split("\\.");
                String[] fields = filename[0].split("-");
                if (fields[0].equals(getBackupName())) {
                    this.checkFields(fields[1].toString());
                }
            }
        }
    }

    /**
     * Algorithm to determine the backup version number
     * @param currNum, current number fed to identify what the backup number should be.
     */
    private void checkFields(String currNum) {
        //System.out.println("Current Number is: " + currNum);//Diagnostics
        this.setCurrIncrementNum(Integer.parseInt(currNum));//Diagnostics
        //System.out.println("Current Big Num is: " + getBiggestIncNum());
        if (this.getCurrIncrementNum() > this.getBiggestIncNum()) {
            this.setBiggestIncNum(this.getCurrIncrementNum());
            //System.out.println("Current Num is now Big Num");//Diagnostics
        }
    }

    /**
     * Method to check if the backup folder is available. If unavailable, it would be created.
     */
    private void checkDir() {
        File dir = new File(this.getBkpath());
        if (!dir.exists()) {
            dir.mkdir();
        }
    }

    /**
     * @return the bkpath
     */
    public String getBkpath() {
        return bkpath;
    }

    /**
     * @param bkpath the bkpath to set
     */
    public void setBkpath(String bkpath) {
        this.bkpath = bkpath;
    }

    /**
     * @return the backupName
     */
    public String getBackupName() {
        return backupName;
    }

    /**
     * @param backupName the backupName to set
     */
    public void setBackupName(String backupName) {
        this.backupName = backupName;
    }

    /**
     * @return the currIncrementNum
     */
    public int getCurrIncrementNum() {
        return currIncrementNum;
    }

    /**
     * @param currIncrementNum the currIncrementNum to set
     */
    public void setCurrIncrementNum(int currIncrementNum) {
        this.currIncrementNum = currIncrementNum;
    }

    /**
     * @return the biggestIncNum
     */
    public int getBiggestIncNum() {
        return biggestIncNum;
    }

    /**
     * @param biggestIncNum the biggestIncNum to set
     */
    public void setBiggestIncNum(int biggestIncNum) {
        this.biggestIncNum = biggestIncNum;
    }

    /**
     * @return the smallestIncNum
     */
    public int getSmallestIncNum() {
        return smallestIncNum;
    }

    /**
     * @param smallestIncNum the smallestIncNum to set
     */
    public void setSmallestIncNum(int smallestIncNum) {
        this.smallestIncNum = smallestIncNum;
    }

    /**
     * @return the res
     */
    public Resource getRes() {
        return res;
    }

    /**
     * @param res the res to set
     */
    public void setRes(Resource res) {
        this.res = res;
    }
}
